package app

import (
	"context"
	"net/http"

	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/lab-online/pkg/auth"
	"github.com/lab-online/pkg/jwt"
	"github.com/lab-online/pkg/logger"
	"github.com/lab-online/pkg/middleware"
	"github.com/lab-online/pkg/mq"
	"github.com/lab-online/pkg/oss"
	"gorm.io/gorm"
)

type Config struct {
	OSS          *oss.Options
	Database     *gorm.DB
	MessageQueue *mq.MessageQueue
	JWT          *jwt.Config             // jwt配置
	CORS         cors.Config             // 跨域配置
	HttpLogger   middleware.LoggerConfig // 日志配置
	Mode         string                  // gin mode
}

type Server interface {
	// Bootstrap 引导服务启动函数 (如迁移数据库、注册路由、初始化服务、端口监听等)
	Bootstrap(addr string, prefix string)
	// Shutdown 关闭服务
	Shutdown()
	// Migrate 数据库迁移
	Migrate() error
	// RegisterRoutes 注册路由
	RegisterRoutes(prefix string)
	// GetEngine 获取gin.Engine
	GetEngine() *gin.Engine
}

type Context struct {
	DB           *gorm.DB
	OSS          oss.OSSManager
	MessageQueue *mq.MessageQueue
	engine       *gin.Engine
	jwt          jwt.JWTAction
	auth         auth.Middleware
	server       *http.Server
	effects      []context.CancelFunc
}

func New(config *Config) Server {
	jwt, _ := jwt.New(config.JWT)

	ctx := &Context{
		DB:           config.Database,
		MessageQueue: config.MessageQueue,
		jwt:          jwt,
		auth:         auth.New(jwt),
		effects:      make([]context.CancelFunc, 0, 10),
	}

	var err error
	if ctx.OSS, err = config.OSS.New(); err != nil {
		logger.Warn("failed to create oss client")
		logger.Warn(err.Error())
	}

	ctx.initEngine(config.Mode)
	ctx.bindGlobalMiddleware(config.CORS, config.HttpLogger)

	return ctx
}

func (app *Context) Bootstrap(addr string, prefix string) {
	if err := app.Migrate(); err != nil {
		logger.Warn("failed to migrate database")
		logger.Warn(err.Error())
	}

	app.buildServer(addr)
	app.RegisterRoutes(prefix)

	go app.Listening(addr)
}

func (app *Context) GetEngine() *gin.Engine {
	return app.engine
}

func (app *Context) initEngine(mode string) {
	gin.SetMode(mode)
	app.engine = gin.New()
	gin.DebugPrintRouteFunc = logger.PrintRouter
	app.engine.NoRoute(middleware.NotFound)
}

func (app *Context) bindGlobalMiddleware(
	corsConfig cors.Config, loggerConfig middleware.LoggerConfig,
) {
	app.engine.Use(
		middleware.Logger(loggerConfig),
		middleware.CORS(corsConfig),
		middleware.Recover(),
	)
}
